package com.zdp.netty.chapter14.codec;

import com.zdp.netty.chapter14.struct.Header;
import com.zdp.netty.chapter14.struct.NettyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * NettMessage消息解码器，解码规则如下：
 * (1)crcCode：通过java.nio.ByteBuffer.getInt()获取校验码字段；
 * (2)length：通过java.nio.ByteBuffer.getInt()获取Netty消息的长度；
 * (3)sessionID：通过java.nio.ByteBuffer.getLong()获取会话ID；
 * (4)type：通过java.nio.ByteBuffer.get()获取消息类型；
 * (5)priority：通过java.nio.ByteBuffer.get()获取消息优先级；
 * (6) attachment: 首先创建一个新的attachment对象，调用java.nio.ByteBuffer.getInt()获取附件的长度，
 *              如果为0，说明附件为空，解码结束，继续解消息体；
 *              如果非空，则根据长度通过for循环进行解码
 * （7）body：通过JBoss的marshaller对其进行解码
 *
 * LengthFieldBasedFrameDecoder : 支持TCP粘包和半包处理，给出表示消息长度的字段偏移量和消息长度(length属性）自身所占的字节数即可
 * @author ZhengDp
 * @Date 2024/4/10 8:16
 */
public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {

    MarshallingDecoder marshallingDecoder;

    public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) throws IOException {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
        this.marshallingDecoder = new MarshallingDecoder();
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf frame = (ByteBuf) super.decode(ctx,in);
        if(frame == null) {
            return null;
        }

        NettyMessage message = new NettyMessage();
        Header header = new Header();
        header.setCrcCode(frame.readInt());
        header.setLength(frame.readInt());
        header.setSessionId(frame.readLong());
        header.setType(frame.readByte());
        header.setPriority(frame.readByte());

        // 判断是否有附件
        int size = frame.readInt();
        if(size > 0) {
            Map<String,Object> attch = new HashMap<>();
            int keySize = 0;
            byte[] keyArray = null;
            String key = null;
            for(int i =0;i<size;i++) {
                keySize = frame.readInt();
                keyArray = new byte[keySize];
                frame.readBytes(keyArray);
                key = new String(keyArray,"UTF-8");
                attch.put(key, marshallingDecoder.decode(frame));
            }
            keyArray = null;
            key = null;
            header.setAttachment(attch);
        }
        // 解析消息体
        if(frame.readableBytes() > 4) {
            message.setBody(marshallingDecoder.decode(frame));
        }
        message.setHeader(header);
        return message;
    }
}
